/*
 * types.c
 *
 * Copyright (C) 2015 - kali
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "include/types.h"

/* atom* readlist (char**);
atom* read (char**); */
void lmd_print (atom*);

atom sect = TBOOL, secf = TBOOL;
atom *t = &sect, *f = &secf;

atom *lmd_newbool(int val) {
	return val ? t : f;
}

/*
acont *newcont(char *respt) {
	acont *ret = malloc(sizeof(acont));
	ret->t = tcont;
	ret->size = bos - respt;
	ret->stack = malloc(ret->size);
	ret->respt = respt;
	//memcpy(ret->stack, respt, ret->size);
	return ret;
}
*/


atom *lmd_newcons(atom *car, atom *cdr) {
	acons *ret = malloc(sizeof(acons));
	ret->t = TCONS;
	ret->car = car;
	ret->cdr = cdr;
	return (atom *) ret;
}

acons *lmd_ccons(atom *x) {
	if (x && *x == TCONS)
		return (acons *) x;
	// I've decided that null is a legit cons cast
	if (x)
	{
		printf("bad cons cast: ");
		lmd_print(x);
	} 
	return NULL;
}



atom *lmd_newint(int i) {
	aint *ret = malloc(sizeof(aint));
	ret->t = TINT;
	ret->i = i;
	return (atom *) ret;
}

aint *lmd_cint(atom *x) {
	//printf("cint FTW!\n");
	if (x && *x == TINT)
		return (aint *) x;
	printf("bad int cast\n");
	return NULL;
}



atom *lmd_newchar(char c) {
	achar *ret = malloc(sizeof(achar));
	ret->t = TCHAR;
	ret->c = c;
	return (atom *) ret;
}

achar *lmd_cchar(atom *x) {
	if (x && *x == TCHAR)
		return (achar *) x;
	printf("bad char cast\n");
	return NULL;
}



atom *lmd_newstring(char *s) {
	char *str = malloc(strlen(s));
	astring *ret = malloc(sizeof(astring));
	ret->t = TSTRING;
	ret->s = strcpy(str, s);
	return (atom *) ret;
}

astring *lmd_cstring(atom *x) {
	if (x && *x == TSTRING)
		return (astring *) x;
	printf("bad string cast\n");
	return NULL;
}



atom *lmd_newsym(char *s) {
	char *str = malloc(strlen(s));
	astring *ret = malloc(sizeof(asym));
	ret->t = TSYM;
	ret->s = strcpy(str, s);
	return (atom *) ret;
}

asym *lmd_csym(atom *x) {
	if (x && *x == TSYM)
		return (asym *) x;
	printf("bad sym cast\n");
	return NULL;
}


 
nspace *lmd_bind(nspace *head, asym *name, atom *link) {
	nspace *ret = malloc(sizeof(nspace));
	ret->next = head;
	ret->name = name;
	ret->link = link;
	return ret;	
}



atom *lmd_newax(atom *(*primitive)(acons*, nspace*)) {
	aax *ret = malloc(sizeof(aax));
	ret->t = TAX;
	ret->primitive = primitive;
	return (atom *) ret;
}

aax *lmd_cax(atom *x)
{
	if (x && *x == TAX)
		return (aax *) x;
	return NULL;
}



atom *lmd_newfun(atom *args, acons *body, nspace *n) {
	afun *ret = malloc(sizeof(afun));
	ret->t = TFUN;
	ret->args = args;
	ret->body = body;
	ret->n = n;
	return (atom *) ret;
}

afun *lmd_cfun(atom *x) {
	if (x && *x == TFUN)
		return (afun *) x;
	printf("bad fun cast\n");
	return NULL;
}



int lmd_eq(atom *a, atom *b) {
	//printf("eq called\n");
	
	if (!a || !b)
		return !a && !b;
	if (*a != *b) return 0;
	
	asym *x, *y;
	
	switch (*a) {
		
		case TINT:
			return lmd_cint(a)->i == lmd_cint(b)->i;
		case TCHAR:
			return lmd_cchar(a)->c == lmd_cchar(b)->c;
		case TSYM:
			x = lmd_csym(a);
			y = lmd_csym(b);
			return !strcmp(x->s, y->s);
		default:
			return a == b;
	}
}

static void lmd_printns (nspace *n) {
	if (n && !lmd_eq(lmd_catom(n->name), lmd_newsym("print")))
	{
		lmd_print(lmd_catom(n->name));
		printf(" -> ");
		lmd_print(n->link);
		printf(", ");
		lmd_printns(n->next);
	}
	//else
	//	printf("end ns");
}


void lmd_print (atom *x) {
	if (!x)
		printf("()");
	else if (*x == TCONS) {
		acons *c = (acons *) x;
		printf("(");
		lmd_print(c->car);
		while (c->cdr && *(c->cdr) == TCONS) {
			c = (acons *) c->cdr;
			printf(" ");
			lmd_print(c->car);
		}
		if (c->cdr) {
			printf(" . ");
			lmd_print(c->cdr);
		}
		printf(")");
	}
	else {
		switch (*x) {
			case TINT: printf("%i", lmd_cint(x)->i); break;
			case TCHAR: printf("#\\%c", lmd_cchar(x)->c); break;
			case TSTRING: printf("\"%s\"", lmd_cstring(x)->s); break;
			case TSYM: printf("%s", lmd_csym(x)->s); break;
			case TBOOL: printf(x == t ? "#t" : "#f"); break;
			case TFUN:
				printf("#fun#");
				break;
				printf("args: [");
				lmd_print(lmd_cfun(x)->args);
				printf("] body: [");
				lmd_print(lmd_catom(lmd_cfun(x)->body));
				printf("] ns: [");
				lmd_printns(lmd_cfun(x)->n);
				printf("]");
				break;
			case TAX:
				printf("#axiom#");
				break;
			case TCONT:
				printf("#cont#");
				break;
			default:
				printf("#Some types#");
				break;
		}
	}
}
